package edu.chalmers.targine;

import java.util.concurrent.ConcurrentHashMap;

import org.cocos2d.layers.CCScene;
import org.cocos2d.nodes.CCDirector;
import org.cocos2d.opengl.CCGLSurfaceView;

import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentFilter.MalformedMimeTypeException;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.NfcAdapter;
import android.nfc.NfcAdapter.CreateNdefMessageCallback;
import android.nfc.NfcEvent;
import android.os.Bundle;
import android.os.Parcelable;
import android.view.Display;
import edu.dhbw.andar.ARToolkit;
import edu.dhbw.andar.AndARActivity;
import edu.dhbw.andar.exceptions.AndARException;
import edu.dhbw.andar.util.CustomRenderer;
import edu.dhbw.andar.util.Model;

/**
 * Example of an application that makes use of the AndAR toolkit.
 * @author Tobi
 *
 */
public abstract class ARManager extends AndARActivity implements ServerConnectionEventListener, CreateNdefMessageCallback, NfcAdapter.OnNdefPushCompleteCallback {
	protected ConcurrentHashMap<String, Arbject> arbjects;
	public static ConcurrentHashMap<String, Model> models;
	protected ARToolkit artoolkit;
	protected String LOCAL_MAC;
	protected int WINDOW_WIDTH;
	protected int WINDOW_HEIGHT;
    
    private NfcAdapter mAdapter;
    private PendingIntent pendingIntent;
    private IntentFilter cardFilter;
    private IntentFilter deviceFilter;
    private IntentFilter[] mIntentFilters;
    
    protected CCScene cocosScene;
    protected CCGLSurfaceView cocosView;
    
    protected ServerConnection serverConnection = new ServerConnection();
    
    @Override
	public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        //Store formatted local mac address as device id
	    WifiManager wifiMan = (WifiManager) this.getSystemService(Context.WIFI_SERVICE);
		WifiInfo wifiInf = wifiMan.getConnectionInfo();
		LOCAL_MAC = wifiInf.getMacAddress();
		LOCAL_MAC = LOCAL_MAC.replace(':', ' ');
		
		//Store window size
		Display display = getWindowManager().getDefaultDisplay();
		Point size = new Point();	
		display.getSize(size);
		WINDOW_WIDTH = size.x;
		WINDOW_HEIGHT = size.y;
		
        setupIntentFilter();
        
		CustomRenderer renderer = new CustomRenderer();//optional, may be set to null
		super.setNonARRenderer(renderer);//or might be omited
		
		artoolkit = super.getArtoolkit();
		arbjects = new ConcurrentHashMap<String, Arbject>();
		models = new ConcurrentHashMap<String, Model>();

		registerModels();
		
		serverConnection.startCommunicatingWithServer();
		serverConnection.addEventListener(this);

		cocosView = new CCGLSurfaceView(this);
        cocosView.setEGLConfigChooser(8, 8, 8, 8, 16, 0);
        cocosView.getHolder().setFormat(PixelFormat.TRANSLUCENT);
        
        frame.addView(cocosView, 0);
        
        startPreview();
    }
    
    protected abstract void registerModels();
    
    protected abstract void registerArbject(String arbjectID, String patternName, String patternFile) throws AndARException;
    
    protected abstract void update();
    
    public void setSharedData(String arbjectID, String var, String value){
    	serverConnection.setSharedData(arbjectID + "." + var, value);
    }
    
	public abstract void serverConnectionEventOccurred(String[] msg);
    
    public String[] getSharedData(String key, String var){
    	return serverConnection.getMessage();
    }
    
    private void setupIntentFilter(){
    	mAdapter = NfcAdapter.getDefaultAdapter(this);
    	// Register callback to set NDEF message
    	mAdapter.setNdefPushMessageCallback(this, this);
    	// Register callback to listen for message-sent success
    	mAdapter.setOnNdefPushCompleteCallback(this, this);
    	
    	pendingIntent = PendingIntent.getActivity(
        	    this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
    	cardFilter = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED);
    	deviceFilter = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
        try {
//            ndef.addDataType("*/*");    /* Handles all MIME based dispatches.
//                                           You should specify only the ones that you need. */
//            cardFilter.addDataType("*/*");
            deviceFilter.addDataType("text/plain");
//            deviceFilter.addDataType("application/edu.chalmers.gos");
        }catch (MalformedMimeTypeException e) {
            throw new RuntimeException("fail", e);
        }
        mIntentFilters = new IntentFilter[]{deviceFilter, cardFilter};
//        mTagFilters = new IntentFilter[]{cardFilter};
//        mNdefIntentFilters = new IntentFilter[]{cardFilter, deviceFilter};
//    	techListsArray = new String[][] { new String[] { NfcF.class.getName() } };
//        intentFiltersArray = new IntentFilter[] {ndef, };
    }
    
    public NdefMessage createNdefMessage(NfcEvent event){
    	NdefRecord record = new NdefRecord(
    		NdefRecord.TNF_MIME_MEDIA,
    		"application/edu.chalmers.gos".getBytes(),
    		new byte[0],
    		LOCAL_MAC.getBytes()
    	);
    	
		NdefMessage msg = new NdefMessage(new NdefRecord[] {
			record, // your data
			NdefRecord.createApplicationRecord(getPackageName()), // the "application record"
		});
		
		return msg;
    }
    
    public void onNdefPushComplete(NfcEvent event) {
		onBeamComplete(event);
	}
    
    @Override
    public void onResume(){
    	super.onResume();
    	CCDirector.sharedDirector().resume();
    	enableNdefExchangeMode();
    }
    
    private void enableNdefExchangeMode(){
    	if(mAdapter != null && mAdapter.isEnabled()){
    		// Register callback to set NDEF message
        	mAdapter.setNdefPushMessageCallback(this, this);
        	// Register callback to listen for message-sent success
        	mAdapter.setOnNdefPushCompleteCallback(this, this);
    			
//			mAdapter.enableForegroundDispatch(this, pendingIntent, mTagFilters, null);
//    		mAdapter.enableForegroundNdefPush(this, msg);
			mAdapter.enableForegroundDispatch(this, pendingIntent, mIntentFilters, null);
//    		mAdapter.enableForegroundDispatch(this, pendingIntent, null, null);
    	}
    }
	
    @Override
    public void onStart(){
    	super.onStart();
    	CCDirector.sharedDirector().attachInView(cocosView);
        CCDirector.sharedDirector().setDisplayFPS(true);
        CCDirector.sharedDirector().setAnimationInterval(1.0f / 60.0f);
        cocosScene = GameLayer.scene();
        CCDirector.sharedDirector().runWithScene(cocosScene);
        ((GameLayer)(cocosScene.getChildren().get(0))).setARManager(this);
    }
    
    @Override
    public void onPause(){
    	CCDirector.sharedDirector().pause();
    	disableNdefExchangeMode();
    	super.onPause();
    }
    
    private void disableNdefExchangeMode() {
    	if(mAdapter != null && mAdapter.isEnabled()){
	        mAdapter.disableForegroundNdefPush(this);
	        mAdapter.disableForegroundDispatch(this);
    	}
    }
    
    @Override
    public void onStop(){
    	CCDirector.sharedDirector().end();
    	serverConnection.removeEventListener(this);
    	serverConnection.stopCommunicatingWithServer();
    	super.onStop();
    }
    
    @Override
    public void onNewIntent(Intent intent){
    	super.onNewIntent(intent);
    	nfcReceived(intent);
    }
    
    private void nfcReceived(Intent intent){
    	byte[] id = new byte[0];
        String action = intent.getAction();
        Parcelable[] rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
        if(rawMsgs != null){
        	NdefMessage msgs = (NdefMessage) rawMsgs[0];
        	String msg = new String(msgs.getRecords()[0].getPayload());
        	beamReceived(msg);
        } else if(NfcAdapter.ACTION_TAG_DISCOVERED.equals(action)){
            id = intent.getByteArrayExtra(NfcAdapter.EXTRA_ID);
            nfcReceived(getArbject(getHex(id)));
        }
    }
    
    protected abstract void onBeamComplete(NfcEvent event);
    
    protected abstract void beamReceived(String deviceMac);
    
    protected abstract void nfcReceived(Arbject arbjcet);
    
    public abstract void performTapAction(String paraType, Object currentAction);
    
    protected abstract void accelerometerChanged(float ax, float ay, float az);

	protected String getHex(byte[] bytes){
		StringBuilder sb = new StringBuilder();
		for(int i = 0; i < bytes.length; i++){
			int b = bytes[i] & 0xff;
			if (b < 0x10){
				sb.append('0');
			}
			sb.append(Integer.toHexString(b));
			if (i != bytes.length - 1) {
				sb.append(" ");
			}
		}
		return sb.toString();
	}

    public void uncaughtException(Thread thread, Throwable ex){
	}
    
    public Arbject getArbject(String arbjectName){
    	synchronized (arbjects) {
    		if(arbjects.containsKey(arbjectName)){
	    		return arbjects.get(arbjectName);
	    	} else{
	    		return null;
	    	}
		}
    }
}
